package com.lighthouse.doc.base.service.impl;

import com.lighthouse.common.enums.DocStatus;
import com.lighthouse.common.tag.service.IKnowledgeTagService;
import com.lighthouse.doc.base.dao.KnowledgeDocBaseInfoRepository;
import com.lighthouse.doc.base.domain.KnowledgeDocBaseInfo;
import com.lighthouse.doc.base.domain.KnowledgeDocBaseInfoVo;
import com.lighthouse.doc.base.domain.SearchInfo;
import com.lighthouse.doc.base.mapper.KnowledgeDocBaseInfoMapper;
import com.lighthouse.doc.base.service.IKnowledgeDocBaseInfoService;
import com.lighthouse.doc.comment.domain.KnowledgeDocCommentVO;
import com.lighthouse.doc.comment.service.IKnowledgeDocCommentService;
import com.lighthouse.doc.operate.service.IKnowledgeDocOperateService;
import com.lighthouse.doc.search.domain.KnowledgeDocSearch;
import com.lighthouse.doc.search.service.IKnowledgeDocSearchService;
import com.lighthouse.doc.view.domain.KnowledgeDocView;
import com.lighthouse.doc.view.service.IKnowledgeDocViewService;
import com.lighthouse.repo.service.IKnowledgeRepoBaseInfoService;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.system.service.ISysUserService;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;

/**
 * 文档信息Service业务层处理
 *
 * @author ysz
 * @date 2023-03-22
 */
@Service
public class KnowledgeDocBaseInfoServiceImpl implements IKnowledgeDocBaseInfoService
{
    @Autowired
    private KnowledgeDocBaseInfoMapper knowledgeDocBaseInfoMapper;

    @Autowired
    private KnowledgeDocBaseInfoRepository knowledgeDocBaseInfoRepository;

    @Autowired
    private ISysUserService sysUserService;

    @Autowired
    private IKnowledgeDocOperateService knowledgeDocOperateService;

    @Autowired
    private IKnowledgeDocCommentService knowledgeDocCommentService;

    @Autowired
    private IKnowledgeRepoBaseInfoService knowledgeRepoBaseInfoService;

    @Autowired
    private IKnowledgeTagService knowledgeTagsService;

    @Autowired
    private IKnowledgeDocViewService knowledgeDocViewService;

    @Autowired
    private IKnowledgeDocSearchService knowledgeDocSearchService;

    @Autowired
    private RedisCache redisCache;

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    /** 文档浏览量 缓存 */
    public static final String KEY_DOC_VIEWS = "doc_views";

    /** 用户最近搜索 缓存 */
    public static final String KEY_RECENT_SEARCH = "recent_search";

    /** 未删除的评论 */
    public static final String COMMENT_NOT_DELETE = "0";

    @Autowired
    private ElasticsearchRestTemplate elasticsearchTemplate;

    /**
     * 查询文档信息
     *
     * @param docId 文档信息主键
     * @return 文档信息
     */
    @Override
    public KnowledgeDocBaseInfoVo selectKnowledgeDocBaseInfoByDocId(String docId)
    {
        KnowledgeDocBaseInfo knowledgeDocBaseInfo;
        if (StringUtils.isEmpty(docId) || "null".equals(docId)) {
            return null;
        }
        try {
            knowledgeDocBaseInfo = knowledgeDocBaseInfoRepository.findById(docId).get();
        } catch (NoSuchElementException e) {
            throw new RuntimeException("没有找到该文档");
        }
        // 缓存浏览情况
        cacheDocViews(docId);
        // 最近浏览情况记录
        knowledgeDocViewService.insertKnowledgeDocView(KnowledgeDocView.builder().docId(docId).userId(SecurityUtils.getUserId()).viewDate(new Date()).build());
        return buildKnowLedgeDocBaseInfoVo(knowledgeDocBaseInfo);
    }

    /**
     * 构建文档展示信息
     */
    private KnowledgeDocBaseInfoVo buildKnowLedgeDocBaseInfoVo(KnowledgeDocBaseInfo knowledgeDocBaseInfo) {
        String docId = knowledgeDocBaseInfo.getDocId();

        KnowledgeDocBaseInfoVo knowledgeDocBaseInfoVo = KnowledgeDocBaseInfoVo.builder().build().copyVoInfo(knowledgeDocBaseInfo);

        knowledgeDocBaseInfoVo.setIsCreater();
        knowledgeDocBaseInfoVo.setOperate(knowledgeDocOperateService.selectKnowledgeDocOperateByDocAndUser(docId), docId);
        knowledgeDocBaseInfoVo.setComments(knowledgeDocCommentService.selectKnowledgeDocCommentListByDocId(KnowledgeDocCommentVO.docCommentVO().docId(docId).build()));

        // 统计文档点赞数
//        knowledgeDocBaseInfoVo.setDocLikes((long) knowledgeDocOperateService.selectKnowledgeDocOperateList(KnowledgeDocOperate.builder().isLike("Y").docId(docId).build()).size());
        return knowledgeDocBaseInfoVo;
    }

    /**
     * 缓存访问量，防止快速访问导致大量io
     * @author ysz
     * @param docId 文档id
     * @return void
     **/
    private void cacheDocViews(String docId) {
        // 浏览人数
        Set users = redisCache.getCacheMap(KEY_DOC_VIEWS).get(docId) == null ? new HashSet() : (Set) redisCache.getCacheMap(KEY_DOC_VIEWS).get(docId);
        // 拒绝刷访问量
        if (CollectionUtils.isEmpty(users) || !users.contains(SecurityUtils.getUserId())) {
            users.add(SecurityUtils.getUserId());
            redisCache.setCacheMap(KEY_DOC_VIEWS, new HashMap<>(){{
                put(docId, users);
            }});
        }
    }

    /**
     * 定时将缓存的浏览量同步进入es，默认30min
     * @author ysz
     * @return void
     **/
    public void storageDocViews() {
        // 使用quartz定时任务的bean获取方式
        RedisCache redisCache = SpringUtils.getBean(RedisCache.class);
        Map<String, Set<Long>> views = redisCache.getCacheMap(KEY_DOC_VIEWS);
        KnowledgeDocBaseInfoRepository knowledgeDocBaseInfoRepository = SpringUtils.getBean(KnowledgeDocBaseInfoRepository.class);
        if (views.size() > 0) {
            for (String docId : views.keySet()) {
                KnowledgeDocBaseInfo knowledgeDocBaseInfo = knowledgeDocBaseInfoRepository.findById(docId).orElse(null);
                if (knowledgeDocBaseInfo == null) {
                    continue;
                }
                knowledgeDocBaseInfo.setDocViews((knowledgeDocBaseInfo.getDocViews() == null ? (long) views.get(docId).size() : knowledgeDocBaseInfo.getDocViews()) + (long) views.get(docId).size());
                knowledgeDocBaseInfoRepository.save(knowledgeDocBaseInfo);
            }
        }
        redisCache.deleteObject(KEY_DOC_VIEWS);
    }

    private void generateViews(String docId, Long userId) {
        knowledgeDocViewService.insertKnowledgeDocView(KnowledgeDocView.builder().docId(docId).userId(userId).viewDate(new Date()).build());
    }

    /**
     * 查询文档信息列表
     *
     * @param knowledgeDocBaseInfo 文档信息
     * @return 文档信息
     */
    @Override
    public List<KnowledgeDocBaseInfo> selectKnowledgeDocBaseInfoList(KnowledgeDocBaseInfo knowledgeDocBaseInfo)
    {
        // 查询目录下的所有文档
        if (StringUtils.isNotEmpty(knowledgeDocBaseInfo.getRepoId())) {
            return knowledgeDocBaseInfoRepository.findByRepoId(knowledgeDocBaseInfo.getRepoId());
        } else if (!CollectionUtils.isEmpty(knowledgeDocBaseInfo.getDocTags())) {
            return knowledgeDocBaseInfoRepository.findByDocStatusAndDocTagsIn(DocStatus.PUBLISHED.getCode(), knowledgeDocBaseInfo.getDocTags());
        }
        return knowledgeDocBaseInfoRepository.findByDocStatus(DocStatus.PUBLISHED.getCode());
    }

    /**
     * 关键词检索文档
     * @param searchInfo 检索信息
     * @return
     */
    @Override
    public List<KnowledgeDocBaseInfoVo> searchKnowledgeDocBaseInfoList(SearchInfo searchInfo) {
        String searchValue = searchInfo.getSearchValue();
        List searchTags = searchInfo.getSearchTags();
        List<KnowledgeDocBaseInfoVo> result = new ArrayList<>();

        // 只检索名称和内容
        BoolQueryBuilder boolQueryBuilderPart = QueryBuilders.boolQuery()
                .should(QueryBuilders.matchQuery("docName",searchValue))
                .should(QueryBuilders.matchQuery("docContentText",searchValue));

        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery().must(boolQueryBuilderPart);
        boolQueryBuilder.must(QueryBuilders.termsQuery("docStatus",DocStatus.PUBLISHED.getCode()));
        if (!CollectionUtils.isEmpty(searchTags)) {
            boolQueryBuilder.must(QueryBuilders.termsQuery("docTags",searchTags));
        }
        // 构建高亮查询
        NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(boolQueryBuilder)
                .withHighlightFields(
                        new HighlightBuilder.Field("docName"),
                        new HighlightBuilder.Field("docContentText"))
                .withHighlightBuilder(new HighlightBuilder().preTags("<strong><font style='color:#d80000'>").postTags("</font></strong>"))
                .build();

        SearchHits<KnowledgeDocBaseInfo> search = elasticsearchTemplate.search(searchQuery, KnowledgeDocBaseInfo.class);
        List<SearchHit<KnowledgeDocBaseInfo>> knowledgeDocBaseInfos = search.getSearchHits();

        // 记录搜索信息
        knowledgeDocSearchService.insertKnowledgeDocSearch(new KnowledgeDocSearch(searchValue));

        if (knowledgeDocBaseInfos.size() > 0) {
            for (SearchHit<KnowledgeDocBaseInfo> searchHit : knowledgeDocBaseInfos) {
                // 展示前端字段
                KnowledgeDocBaseInfoVo knowledgeDocBaseInfoVo = KnowledgeDocBaseInfoVo.builder().build().copyVoInfo(searchHit.getContent());

                // 填充高亮字段
                if (searchHit.getHighlightField("docName").size() > 0) {
                    knowledgeDocBaseInfoVo.setDocName(searchHit.getHighlightField("docName").get(0));
                }
                if (searchHit.getHighlightField("docContentText").size() > 0) {
                    String docContentText = searchHit.getHighlightField("docContentText").get(0);
                    String heightLightText = docContentText.substring(docContentText.indexOf("<strong>"));
                    knowledgeDocBaseInfoVo.setDocContentText(heightLightText);
                }
                result.add(knowledgeDocBaseInfoVo);
            }
        }
        return result;
    }

    /**
     * 新增文档信息
     *
     * @param knowledgeDocBaseInfo 文档信息
     * @return 结果
     */
    @Override
    public KnowledgeDocBaseInfo insertKnowledgeDocBaseInfo(KnowledgeDocBaseInfo knowledgeDocBaseInfo)
    {
        if (StringUtils.isEmpty(knowledgeDocBaseInfo.getDocId())) {
            knowledgeDocBaseInfo.setCreateInfo();
        } else {
            knowledgeDocBaseInfo.setUpdateInfo();
        }
        return knowledgeDocBaseInfoRepository.save(knowledgeDocBaseInfo) == null ? null : knowledgeDocBaseInfo;
    }

    /**
     * 修改文档信息
     *
     * @param knowledgeDocBaseInfo 文档信息
     * @return 结果
     */
    @Override
    public int updateKnowledgeDocBaseInfo(KnowledgeDocBaseInfo knowledgeDocBaseInfo)
    {
        return knowledgeDocBaseInfoMapper.updateKnowledgeDocBaseInfo(knowledgeDocBaseInfo);
    }

    /**
     * 批量删除文档信息
     *
     * @param docIds 需要删除的文档信息主键
     * @return 结果
     */
    @Override
    public int deleteKnowledgeDocBaseInfoByDocIds(String[] docIds)
    {
        return knowledgeDocBaseInfoRepository.deleteByDocIdIn(Arrays.asList(docIds));
    }

    /**
     * 删除文档信息信息
     *
     * @param docId 文档信息主键
     * @return 结果
     */
    @Override
    public int deleteKnowledgeDocBaseInfoByDocId(String docId)
    {
        return knowledgeDocBaseInfoMapper.deleteKnowledgeDocBaseInfoByDocId(docId);
    }

    /**
     * 查询最近关注文档
     * @param noticer 用户id
     * @return 文档信息集合
     */
    @Override
    public List<KnowledgeDocBaseInfo> selectKnowledgeDocRecentAttention(Long noticer, String attentionType) {
        List<String> docIds = knowledgeDocOperateService.selectKnowledgeDocOperateRecent(noticer, attentionType);
        return getSortDocContent(knowledgeDocBaseInfoRepository.findByDocStatusAndDocIdIn(DocStatus.PUBLISHED.getCode(), docIds));
    }

    /**
     * 查询最近浏览文档
     * @param noticer 用户id
     * @return 文档信息集合
     */
    @Override
    public List<KnowledgeDocBaseInfo> selectKnowledgeDocRecentBrowse(Long noticer) {
        List<String> docIds = knowledgeDocViewService.selectKnowledgeDocViewRecent(noticer);
        return getSortDocContent(knowledgeDocBaseInfoRepository.findByDocStatusAndDocIdIn(DocStatus.PUBLISHED.getCode(), docIds));
    }

    /**
     * 我的发布
     * @param owner 用户id
     * @return 文档信息集合
     */
    @Override
    public List<KnowledgeDocBaseInfo> selectKnowledgeDocByStatus(String docStatus, Long owner) {
        return getSortDocContent(knowledgeDocBaseInfoRepository.findByDocStatusAndCreateUser(docStatus, owner));
    }

    /**
     * 热榜
     * @return 文档信息集合
     */
    @Override
    public List<KnowledgeDocBaseInfo> selectKnowledgeDocHot() {
        Sort sort = Sort.by(Sort.Direction.DESC,"docLikes", "docViews", "docCollects");
        //当前页，第一页从 0 开始， 1 表示第二页
        int currentPage=0;
        //每页显示多少条
        int pageSize = 10;
        PageRequest pageRequest = PageRequest.of(currentPage, pageSize,sort);
        return getSortDocContent(knowledgeDocBaseInfoRepository.findByDocStatus(DocStatus.PUBLISHED.getCode(), pageRequest));
    }

    /**
     * 推荐
     * @return 文档信息集合
     */
    @Override
    public List<KnowledgeDocBaseInfo> selectKnowledgeDocSuggest() {
        // 任一有操作记录的文档集（即感兴趣的文档集）
        List<String> docIds = knowledgeDocOperateService.selectKnowledgeDocOperateAnyDone(SecurityUtils.getUserId());
        List<KnowledgeDocBaseInfo> knowledgeDocBaseInfos = knowledgeDocBaseInfoRepository.findByDocStatusAndDocIdIn(DocStatus.PUBLISHED.getCode(), docIds);
        Set tagIds = new HashSet();
        knowledgeDocBaseInfos.stream().forEach(knowledgeDocBaseInfo -> {
            tagIds.addAll(knowledgeDocBaseInfo.getDocTags());
        });
        knowledgeDocBaseInfos = knowledgeDocBaseInfoRepository.findByDocStatusAndDocTagsInAndDocIdNotIn(DocStatus.PUBLISHED.getCode(), new ArrayList<>(tagIds), docIds);
        return knowledgeDocBaseInfos;
    }

    @Override
    public boolean updateDocAvatar(Long userId) {
//        elasticsearchTemplate.bulkUpdate();
        return false;
    }

    /**
     * 缓存搜索历史记录
     * @author ysz
     * @param
     * @return void
     **/
//    private void cacheSearchHistory(String searchInfo) {
//        Long curUser = SecurityUtils.getUserId();
//        Map<String, Object> recentSearchMap = redisCache.getCacheMap(KEY_RECENT_SEARCH);
//
//        Set recentSearch = new HashSet<>();
//        if (recentSearchMap.containsKey(curUser)) {
//            recentSearch = (Set) recentSearchMap.get(curUser);
//        }
//        recentSearch.add(searchInfo);
//
//        redisCache.setCacheMapValue(KEY_RECENT_SEARCH, String.valueOf(curUser), recentSearch);
////        redisCache.expire(KEY_RECENT_SEARCH, 30000);
//    }

    /**
     * 获取简短文档内容（解决前端页面展示问题）
     * @param knowledgeDocBaseInfos
     * @return
     */
    public List<KnowledgeDocBaseInfo> getSortDocContent(List<KnowledgeDocBaseInfo> knowledgeDocBaseInfos) {
        knowledgeDocBaseInfos.stream().forEach(knowledgeDocBaseInfo -> {
            int end = 100;
            if (knowledgeDocBaseInfo.getDocContentText().length() < end) {
                end = knowledgeDocBaseInfo.getDocContentText().length();
            }
            knowledgeDocBaseInfo.setDocContentText(knowledgeDocBaseInfo.getDocContentText().substring(0,end));
        });
        return knowledgeDocBaseInfos;
    }
}
